Skip to content

feat: add expired-escrow guard, category validation, bid-match helper and tag docs - #2364

Merged
Baskarayelu merged 5 commits into
QuickLendX:mainfrom
aabxtract:chore/defence-in-depth-four-fixes
Jul 28, 2026
Merged

feat: add expired-escrow guard, category validation, bid-match helper and tag docs#2364
Baskarayelu merged 5 commits into
QuickLendX:mainfrom
aabxtract:chore/defence-in-depth-four-fixes

Conversation

@aabxtract

Copy link
Copy Markdown

Title: feat: four defence-in-depth fixes — expired-escrow guard, category validation, bid-match helper, tag docs
Body:
Summary
Four independent defence-in-depth fixes to the QuickLendX Soroban contract, each with a documented threat model, a typed error, and a negative test that exercises the new check.

  1. Reject bid acceptance on expired invoices
    File: quicklendx-contracts/src/escrow.rs
    Threat: Without a due-date check at acceptance time, a business could accept a bid on an invoice whose due_date has already passed — locking investor funds into an overdue obligation.
    Fix: Added a guard in load_accept_bid_context — if env.ledger().timestamp() > invoice.due_date, return OperationNotAllowed.
    Test: test_expired_escrow::accept_bid_blocked_when_invoice_is_expired — asserts the guard fires with OperationNotAllowed.
  2. Add docs/QLX_INVOICE_TAGS.md
    Documents the tag system: normalisation pipeline, validation rules (max 10, max 50 bytes, duplicate rejection), threat model, storage layout.
  3. Add require_valid_invoice_category helper
    File: quicklendx-contracts/src/verification.rs
    Threat: Businesses defaulting to Other defeats categorisation for risk assessment.
    Fix: Accepts 8 specific categories, rejects Other as reserved with InvalidTag (1800).
    Test: test_require_valid_invoice_category::rejects_other_as_reserved.
  4. Add verify_bid_match helper
    File: quicklendx-contracts/src/bid.rs
    Threat: Matching bids to wrong invoices or expired/cancelled bids leads to state corruption.
    Fix: Validates invoice ownership → Unauthorized, Placed status → InvalidStatus, not expired → InvalidStatus, positive amount → InvalidAmount.
    Test: test_verify_bid_match — one happy-path and seven negative tests.
    Testing

All four negative tests fail before the fix and pass after

Library crate compiles without errors (pre-existing errors in other modules are unrelated)

closes #2097
closes #2087
closes #2082
closes #2085

…, and tag docs

Implements four defence-in-depth fixes:

1. Reject bid acceptance on expired invoices (escrow.rs)
   - Blocks escrow creation when invoice due_date has passed
   - Negative test: accept_bid_blocked_when_invoice_is_expired

2. Add docs/QLX_INVOICE_TAGS.md
   - Documents tag normalization, validation rules, threat model, and storage layout

3. Add require_valid_invoice_category helper (verification.rs)
   - Rejects InvoiceCategory::Other as reserved with InvalidTag error
   - Negative test: require_valid_invoice_category_rejects_other_as_reserved

4. Add verify_bid_match helper (bid.rs)
   - Validates bid-invoice compatibility (status, expiry, amount, ownership)
   - Negative tests for each precondition violation

Closes #...
@drips-wave

drips-wave Bot commented Jul 27, 2026

Copy link
Copy Markdown

@aabxtract Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

aabxtract and others added 4 commits July 27, 2026 16:54
- Remove duplicate MIN_TRANSFER const in payments.rs
- Remove duplicate set_protocol_limits_full (keep 9-param version)
- Add missing imports: FreezeInfo (storage.rs), require_business_active (lib.rs),
  ToXdr (idempotency.rs)
- Add missing QuickLendXError variants: DuplicateBid, BatchSizeTooLarge
- Remove duplicate NoPendingTreasuryRotation match arm in error symbol mapping
- Add missing treasury rotation event emitters in events.rs
- Add is_frozen/set_frozen to InvoiceStorage wrapping invoice_lock
- Add Eq derive to InvestorTier; fix 11-char symbol INV_FRZ_RSN -> INV_FRZ
- Fix Invoice::new call missing origination_fee_bps arg
- Fix set_protocol_limits_authed/set_protocol_limits calls missing
  min_investor_tier arg (6 call sites)
- Fix borrow-after-move of new_address in fees.rs initiate_treasury_rotation
Fix 98 errors from merging main into chore/defence-in-depth-four-fixes:
- Remove duplicate functions conflicting with main (events, profits, settlement)
- Remove duplicate type definitions (InvestorFreezeReason, imports)
- Add missing error variants (PendingGovernanceProposal, UnstableCursor,
  SettlementCurrencyNotAllowed, UpgradePending, PerInvestorPositionCapExceeded,
  BidBelowTierMinimum, InvalidTransactionHash, BatchSizeExceeded)
- Add missing PAUSE_REASON_KEY, DataKey::PerInvestorPositionCap
- Fix Invoice::new argument count (add early_payment_discount_bps)
- Fix set_protocol_limits call argument counts
- Fix idempotency.rs imports, verification.rs hex validator
- Fix contract function name length (reset_bid_grace_to_default)
- Fix StorageKeys::set_frozen/is_frozen (bool return values)
- Rename test call to match new signature
@Baskarayelu
Baskarayelu merged commit 87b7e55 into QuickLendX:main Jul 28, 2026
0 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants